//
//  AKOTStateMaintainer.m
//  AKMac
//
//  Created by Ben Chen on 6/3/08.
//  Copyright 2008 Sleeper. All rights reserved.
//

#import "AKOTStateMaintainer.h"

enum {
	_OTLeft			= -1,
	_OTUpperLeft	= -9,
	_OTUp			= -8,
	_OTUpperRight	= -7,
	_OTRight		= 1,
	_OTLowerRight	= 9,
	_OTLow			= 8,
	_OTLowerLeft	= 7,
	_OTNumOfDir		= 8
};

int _AllDirections[] = { _OTLeft, _OTUpperLeft, _OTUp, _OTUpperRight, _OTRight, _OTLowerRight, _OTLow, _OTLowerLeft };

typedef int _AKOTDir;

@interface AKOTStateMaintainer ()
@property (readwrite, copy) NSArray *eliminatedPieces, *createdPieces, *movedPieces, *winners;
@property (readwrite, assign) AKSideIndex nextPlayerSide;
@end

#ifndef AKIPHONE
inline AKSideIndex _opponentSideForSide(AKSideIndex aSide) 
#else
AKSideIndex _opponentSideForSide(AKSideIndex aSide)
#endif
{
	AKSideIndex retVal = AKOTBlack;
	if (aSide == AKOTBlack)
		retVal = AKOTWhite;
	return retVal;
}
	
@implementation AKOTStateMaintainer

@synthesize eliminatedPieces, createdPieces, movedPieces, winners, nextPlayerSide, allPieces;

- (id)initWithCoder:(NSCoder*)coder {
	// TODO:
	return nil;
}

- (void)encodeWithCoder:(NSCoder*)coder {
	// TODO
}

- (NSArray*)consecutivePiecesOfSide:(AKSideIndex)side startingCell:(AKCellIndex)srcCell direction:(_AKOTDir)dir {
	NSMutableArray *retVal = [NSMutableArray array];
	
	int cell = srcCell;
	BOOL foundOpponentPieceAtTheEnd = NO;
	while (1) {
		cell+=dir;
		if (cell >= 0 && cell <= AKOTNumOfCells) {
			if (pieces[cell].side == side) {
				NSValue *piece = [NSValue valueWithBytes:&(pieces[cell]) objCType:@encode(AKPiece)];
				[retVal addObject:piece];
			} else if (pieces[cell].side == _opponentSideForSide(side)) {
				foundOpponentPieceAtTheEnd = YES;
				break;
			} else { // == NSNotFound
				break;
			}
		} else {
			break;
		}
	}
	
	if (![retVal count])
		retVal = nil;
	
	if (!foundOpponentPieceAtTheEnd)
		[retVal removeAllObjects];
	
	return retVal;
}

- (NSArray*)allConsecutivePiecesOfSide:(AKSideIndex)side startingCell:(AKCellIndex)srcCell {
	NSMutableArray *retVal = [NSMutableArray array];
	
	int i;
	for (i = 0; i < _OTNumOfDir; ++i) {
		NSArray *piecesAtOneDir = [self consecutivePiecesOfSide:side startingCell:srcCell direction:_AllDirections[i]];
		[retVal addObjectsFromArray:piecesAtOneDir];
	}
	
	return retVal;
}

- (NSMutableArray*)opponentPiecesForPieces:(NSArray*)oriPieces {
	NSMutableArray *retVal = [NSMutableArray array];
	
	for (NSValue *aPiece in oriPieces) {
		AKPiece oriPiece;
		[aPiece getValue:&oriPiece];
		oriPiece.side = _opponentSideForSide(oriPiece.side);
		pieces[oriPiece.cell].side = oriPiece.side;	// FIXME: this should not be here
		[retVal addObject:[NSValue valueWithBytes:&oriPiece objCType:@encode(AKPiece)]];
	}
	
	return retVal;
}

- (BOOL)canPlayerOfSideMakeMove:(AKSideIndex)side {
	BOOL retVal = NO;
	
	int i;
	for (i = 0; i < AKOTNumOfCells; ++i) {
		if (pieces[i].piece == NSNotFound) {
			int j;
			for (j = 0; j < _OTNumOfDir; ++j) {
				if ([[self consecutivePiecesOfSide:_opponentSideForSide(side)
									  startingCell:pieces[i].cell 
										 direction:_AllDirections[j]] count])
					break;
			}
			
			if (j != _OTNumOfDir) {
				retVal = YES;
				break;
			}				
		}
	}
	
	return retVal;
}

- (NSArray*)beginningPieces {
	AKPiece black1, black2, white1, white2;
	black1.cell = 28;
	black1.piece = 0;
	black1.side = AKOTBlack;
	black2.cell = 35;
	black2.piece = 0;
	black2.side = AKOTBlack;
	white1.cell = 27;
	white1.piece = 0;
	white1.side = AKOTWhite;
	white2.cell = 36;
	white2.piece = 0;
	white2.side = AKOTWhite;
	
	return [NSArray arrayWithObjects:
			[NSValue valueWithBytes:&white1 objCType:@encode(AKPiece)], 
			[NSValue valueWithBytes:&black1 objCType:@encode(AKPiece)],
			[NSValue valueWithBytes:&black2 objCType:@encode(AKPiece)], 
			[NSValue valueWithBytes:&white2 objCType:@encode(AKPiece)], 
			nil];
}

- (void)backToInitialState {
	
	int i;
	for (i = 0; i < AKOTNumOfCells; ++i) {
		pieces[i].cell	=	i;
		pieces[i].piece	=	NSNotFound;
		pieces[i].side	=	NSNotFound;
	}
	
	pieces[28].piece	= 0;
	pieces[28].side		= AKOTBlack;
	pieces[35].piece	= 0;
	pieces[35].side		= AKOTBlack;
	
	pieces[27].piece	= 0;
	pieces[27].side		= AKOTWhite;
	pieces[36].piece	= 0;
	pieces[36].side		= AKOTWhite;
	
	self.createdPieces		= [self beginningPieces];		// four initial pieces at the center
	
	self.eliminatedPieces	= nil;
	
	self.nextPlayerSide	= AKOTBlack;	// black goes first
	self.winners		= nil;
	
	self.movedPieces		= nil;		// this should be always nil
}

- (NSArray*)findWinner {
	NSArray *retVal = nil;
	
	unsigned int numOfBlack = 0;
	unsigned int numOfWhite = 0;
	int i;
	for (i = 0; i < AKOTNumOfCells; ++i) {
		if (pieces[i].side == AKOTBlack)
			numOfBlack++;
		else if (pieces[i].side == AKOTWhite)
			numOfWhite++;
	}
	
	if (numOfBlack == numOfWhite)
		retVal = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:AKOTBlack], [NSNumber numberWithUnsignedInt:AKOTWhite], nil];
	else if (numOfBlack > numOfWhite)
		retVal = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:AKOTBlack]];
	else
		retVal = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:AKOTWhite]];
	return retVal;
}

- (BOOL)performMove:(AKMove)newMove {
	BOOL retVal = YES;
	
	if (newMove.srcCell != NSNotFound || [winners count] != 0 || newMove.dstCell > AKOTNumOfCells || 
		newMove.side != nextPlayerSide || newMove.piece > 0) {
		retVal = NO;
	} else {
		AKPiece *pieceInfo = &pieces[newMove.dstCell];
		
		if (pieceInfo->piece != NSNotFound) {
			retVal = NO;	// the destination cell must be empty
		} else {
			pieceInfo->cell		= newMove.dstCell;
			pieceInfo->piece	= newMove.piece;
			pieceInfo->side		= newMove.side;
			
			self.eliminatedPieces = [self allConsecutivePiecesOfSide:_opponentSideForSide(newMove.side) startingCell:newMove.dstCell];
			
			if (![self.eliminatedPieces count]) {
				[NSException raise:NSInternalInconsistencyException format:@"Eliminated Pieces is nil"];
			}
			
			NSMutableArray *replacedPieces = [self opponentPiecesForPieces:self.eliminatedPieces];
			NSValue *aPiece = [NSValue value:pieceInfo withObjCType:@encode(AKPiece)];
			[replacedPieces addObject:aPiece];
			self.createdPieces	= replacedPieces;
			
			if ([self canPlayerOfSideMakeMove:_opponentSideForSide(self.nextPlayerSide)])
				self.nextPlayerSide = _opponentSideForSide(self.nextPlayerSide);
			else if (![self canPlayerOfSideMakeMove:self.nextPlayerSide]) {
				self.winners = [self findWinner];
			}
		}
	}
	
	return retVal;
}

- (NSArray*)reachableCellsForPiece:(AKPiece)piece {
	return nil;
}

- (NSArray*)newPiecesAtCell:(AKCellIndex)cell {
	NSArray *retVal = nil;
	
	if (cell < AKOTNumOfCells && pieces[cell].piece == NSNotFound) {
		if ([[self allConsecutivePiecesOfSide:_opponentSideForSide(self.nextPlayerSide) startingCell:cell] count])
			retVal = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:0]];		
	}
	
	return retVal;
}

- (AKPiece)moveablePieceAtCell:(AKCellIndex)cell {
	return AKZeroPiece;
}

@end
